package com.fagejiang.gateway.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

import static java.util.Collections.synchronizedMap;

/**
 * TODO
 *
 * @author 发哥讲Java
 * @version 1.0
 * @date 2021-05-17 14:19
 */
@Slf4j
@Component
public class CustomRouteConfig implements RouteDefinitionRepository, Ordered, ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

    @NotNull
    @Valid
    private final static Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());

    @PostConstruct
    private void loadRouteDefinition() {
        log.error("PostConstruct loadRouteDefinition!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId("test01");
        try {
            routeDefinition.setUri(new URI("http://localhost:8080"));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        List<PredicateDefinition> predicates = new ArrayList<>();
        PredicateDefinition p1 = new PredicateDefinition();
        Map<String, String> args = new HashMap<>();
        args.put("Path", "/get2");
        p1.setArgs(args);
        p1.setName("Path");
        predicates.add(p1);
        routeDefinition.setPredicates(predicates);
        routes.put("test01", routeDefinition);
    }

    @Override
    public int getOrder() {
        return -1121;
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        log.info("进入到 CustomRouteConfig getRouteDefinitions");
        return Flux.fromIterable(routes.values());
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {

        log.info("进入到 CustomRouteConfig save");
        return route.flatMap(r -> {
            System.out.println("0000000000");
            if (StringUtils.isEmpty(r.getId())) {
                System.out.println("11111");
                return Mono.error(new IllegalArgumentException("id may not be empty"));
            }
            System.out.println("2222");
            routes.put(r.getId(), r);
            return Mono.empty();
        });
    }

    public void add() {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId("test02");
        try {
            routeDefinition.setUri(new URI("http://localhost:8080"));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        List<PredicateDefinition> predicates = new ArrayList<>();
        PredicateDefinition p1 = new PredicateDefinition();
        Map<String, String> args = new HashMap<>();
        args.put("Path", "/get3");
        p1.setArgs(args);
        p1.setName("Path");
        predicates.add(p1);
        routeDefinition.setPredicates(predicates);
        routes.put(routeDefinition.getId(), routeDefinition);
        System.out.println("routes.size() = " + routes.size());

        this.save(Mono.just(routeDefinition)).subscribe();
        this.doLoad();
    }

    /**
     * 重新刷新 路由
     */
    public String doLoad() {
        try {
            log.error("刷新!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        } catch (Exception e) {
            e.printStackTrace();
            return "load fail";
        }
        return "load success";
    }


    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        log.info("进入到 CustomRouteConfig delete");
        return routeId.flatMap(id -> {
            if (routes.containsKey(id)) {
                routes.remove(id);
                return Mono.empty();
            }
            return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
        });
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {

        this.publisher = applicationEventPublisher;
    }

    public void delete(String id) {
        try {
            this.delete(Mono.just(id)).subscribe();
            this.doLoad();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("delete fail,not find route  routeId: " + id);
        }
        log.info("delete success");

    }
}
